package Game;


import Cards.Deck;
import Cards.UnoCard;
import Internationalization.LoadInternationalization;
import JDBC.JavaDataBase;
import Resources1.Settings;
import UnoBots.BotPlayerUNO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;

/**
 * Class UnoGame. It manages UnoGame
 */

public class UnoGame extends Game<UnoCard, PlayerUNO> {
    private int cardsToTake; //Variable estática que almacena la acumulación de las cartas por robar.
    private UnoCard.Colour colour;
    private boolean robado;
    private Map<PlayerUNO, Integer> playersPoints;
    private int idPartida;
    private JavaDataBase jdb;
    public UnoGame(){
        super();
        cardsToTake = 0;
        robado = false;
        playersPoints= new HashMap<>();
        jdb = new JavaDataBase();
        PlayerUNO pAux;
        BotPlayerUNO bAux;
        ArrayList<String> jugadores = new ArrayList<>();
        ArrayList<String> bots = new ArrayList<>();
        //Cargamos la baraja
        loadDeck();
        try{
            jugadores = Settings.getPlayersUNO();
            bots = Settings.getBotsUNO();
        }
        catch(Exception e){
            System.out.println(e.toString());
        }
        
        for (int i = 0; i<jugadores.size(); i++){
            pAux = new PlayerUNO(jugadores.get(i));
            players.add(pAux);
            //jdb.insertarJugador(pAux);
        }
        for (int i = 0; i<bots.size(); i++){
            bAux = BotPlayerUNO.getInstance(bots.get(i), i);
            players.add(bAux);
            //jdb.insertarBot(bAux);
        }
        jdb.insertStart(players.size(),"UNO");
        jdb.disconnect();
        //jdb.insertarJugador(players.get(0).getId(), "PLAYER");
    }
    
    /**
    * This method starts the game
    */
    
    public void startGame(){
        ListIterator<PlayerUNO> it = players.listIterator();
        boolean reverse = false;
        PlayerUNO player = new PlayerUNO(""); // Se inicializa solo porque se queja el compilador
        dealHand();
        //Colocamos una carta en lo alto de la pila
        playedCards.add(deck.takeCard());
        //Comprobamos que no sea roba 4, en cuyo caso se sacará otra
        while (playedCards.getFirst().getRank().equals(UnoCard.Number.TAKEFOUR)){
            deck.add(deck.size()/2, playedCards.getFirst());
            playedCards.removeFirst();
            playedCards.add(deck.takeCard());
        }
        //Si es un "Roba2" el primer jugador tendrá 2 cartas pendientes por robar.
        if (playedCards.getFirst().getRank().equals(UnoCard.Number.TAKETWO)){
            cardsToTake += 2;
        }
        if (playedCards.getFirst().getRank().equals(UnoCard.Number.COLOURCHANGE)){
            player = it.next();
            player.changeColour();
            it.previous();
        }
        while (!endGame()){
            //Si los turnos van en sentido contrario
            if (reverse){
                if (!it.hasPrevious()){
                    it = players.listIterator(players.size());
                }
                player = it.previous();
            }
            //Si los turnos van en sentido normal
            else if (!reverse){
                if (!it.hasNext()){
                    it = players.listIterator();
                }
                player = it.next();
            }
            //System.out.println("Se ha jugado la carta: " + this.getTopCard());
            System.out.println(LoadInternationalization.getRB().getString("playedCard") +getTopCard());
            if (this.getTopCard().getRank().equals(UnoCard.Number.COLOURCHANGE)){
                System.out.println(LoadInternationalization.getRB().getString("chosenColour"));
            }
            player.myTurn(this);
            //Controlar aquí la carta que se ha jugado, en caso de que sea especial
            //o comodín hacer el efecto correspondiente.
            if (this.getTopCard().isJoker() || this.getTopCard().isSpecialCard() ){
                switch (this.getTopCard().getRank()){
                    case TAKETWO:
                        if (!robado){
                            cardsToTake +=2;
                            colour = getTopCard().getSuit();
                        }
                        break;
                    case REVERSE:
                        if (!reverse){
                            player = it.previous();
                            reverse = true;
                        }
                        else{
                            player = it.next();
                            reverse = false;
                        }
                        colour = getTopCard().getSuit();
                        break;
                    case NEXT:
                        if (reverse){
                            if (!it.hasPrevious()){
                                it = players.listIterator(players.size());
                            }
                            it.previous();
                        }
                        else{
                            if (!it.hasNext()){
                                it = players.listIterator();
                            }
                            it.next();
                        }
                        colour = getTopCard().getSuit();
                        break;
                    case TAKEFOUR:
                        if (!robado){
                            cardsToTake += 4;
                            setRobado(false);
                            colour = player.changeColour();
                            System.out.println(LoadInternationalization.getRB().getString("colourChanged") + this.colour);
                        }
                        break;
                    case COLOURCHANGE:
                        colour = player.changeColour();
                        break;
                    default:
                        System.err.println(LoadInternationalization.getRB().getString("error"));
                        break;
                }
            }
            else{
                colour = getTopCard().getSuit();
            }
        }
        countPoints();
    }
    /**
    * This method check if the Game is end
    * @return true if the Game is end or false in the wrong case
    */
    private boolean endGame(){
        if (deck.isEmpty()){
            return true;
        }
        else{
            for (PlayerUNO p : players){
                if (p.handIsEmpty()){
                    return true;
                }
            }
            return false;
        }
    }
    
    /**
    * This method counts the Points of a hand 
    */
    private void countPoints(){
        int acum = 0;
        for (PlayerUNO p : players){
            for (UnoCard c : p.getHand()){
                acum += c.points();
            }
            playersPoints.put(p, acum);
            acum = 0;
        }
        System.out.println(LoadInternationalization.getRB().getString("pointsFollows"));
        System.out.println(playersPoints);
        
//        jdb.insertarPartida(this);
    }
    
    /**
     * This method gets the card to take
     *@return cardsToTake 
     */
    public int getCardsToTake(){
        return cardsToTake;
    }
    
    /**
     * This method sets cards to take
     */
    
    public void setCardsToTake(int cardsToTake){
        this.cardsToTake = cardsToTake;
    }
    
    /**
     * This method gets the colour of a card
     *@return colour 
     */
    public UnoCard.Colour getColour(){
        return colour;
    }
    
    /**
     * This method sets that the last player has taken a card
     * @param b
     */
    
    public void setRobado(boolean b){
        this.robado = b;
    }
    
    /**
     * This method checks if the last player has taken a card
     *@return robado 
     */
    
    public boolean getRobado(){
        return robado;
    }
    
    /**
     *This method gets the number of players
     *@return players.size() 
     */
    
    public int getNumberOfPlayers(){
        System.out.println("PLAYERS VALE: " + players.size());
        return players.size();
    }
    
    public Map<PlayerUNO, Integer> getPlayersPoints(){
        return playersPoints;
    }
    
    /**
     * This method get the id of the game
     * @return idPartida
     */
    public int getIdPartida (){
        return idPartida;
    }
    
     /**
     * This method loads the deck
     */
    private void loadDeck(){
        try {
            Deck<UnoCard> d = new Deck<>();
            UnoCard uc;
            String[] card;
            String str;
            InputStream is = getClass().getResourceAsStream("/Resources1/unoDeck.txt");
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            while ((str = br.readLine()) != null){
                card = str.split("\\s");
                uc = new UnoCard(UnoCard.Number.valueOf(card[0]), UnoCard.Colour.valueOf(card[1]));
                d.add(uc);
            }
            Collections.shuffle(d);
            this.deck = d;
            
            //En this.deck tenemos almacenada la baraja completa barajada.
        } catch (IOException ex) {
            System.err.println(ex.toString());
        }
    }
    
     /**
     * This method deals a hand
     */
    
    private void dealHand(){
        //Repartimos las cartas a cada jugador
        for (PlayerUNO p : players){
            for (int i = 0; i<7; i++){
                p.addCardToHand(deck.takeCard());
            }
        }
    }
}
